[Py-Intro] Aula 03

Tipos básicos

O que você vai aprender nesta aula?

Após o término da aula você terá aprendido:

  • Como manipular listas e tuplas

Listas

Na aula passada vimos o básico de lista, o objetivo desta seção é se aprofundar um pouco mais nos usos de lista.


In [123]:
numeros = [1, 2, 3, 4]
numeros


Out[123]:
[1, 2, 3, 4]

Tamanho da lista:


In [124]:
len(numeros)  # a função len funciona para todas as sequências


Out[124]:
4

Concatenando listas:


In [125]:
numeros + [5, 6, 7, 8]


Out[125]:
[1, 2, 3, 4, 5, 6, 7, 8]

In [126]:
numeros


Out[126]:
[1, 2, 3, 4]

Para modificar a lista é necessário usar +=:


In [127]:
numeros += [5, 6, 7, 8]
numeros


Out[127]:
[1, 2, 3, 4, 5, 6, 7, 8]

Para anexar elementos no final da lista pode-se usar list.append():


In [128]:
numeros


Out[128]:
[1, 2, 3, 4, 5, 6, 7, 8]

In [129]:
numeros.append(9)
numeros


Out[129]:
[1, 2, 3, 4, 5, 6, 7, 8, 9]

É possível ter listas aninhadas:


In [130]:
aninhada = [1, 2, 3, [4, 5, 6]]
aninhada


Out[130]:
[1, 2, 3, [4, 5, 6]]

In [131]:
aninhada[3]


Out[131]:
[4, 5, 6]

In [132]:
aninhada[3][2]


Out[132]:
6

É possível repetir uma lista usando o operador *:


In [133]:
zeros = [0] * 10
zeros


Out[133]:
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0]

In [134]:
[1, 2, 3] * 5


Out[134]:
[1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3]

Funções de lista

lista.insert(i, x) - Insere um item em uma posição:


In [135]:
lista = [1, 2, 4, 5]
lista


Out[135]:
[1, 2, 4, 5]

In [136]:
lista.insert(2, 3)
lista


Out[136]:
[1, 2, 3, 4, 5]

list.remove(i) ou del list[i] - Remove um item de uma posição


In [137]:
lista = [1, 2, 3, 4, 5]
del lista[0]
lista


Out[137]:
[2, 3, 4, 5]

In [138]:
lista.remove(3)
lista


Out[138]:
[2, 4, 5]

list.pop([i]) - Remove o item na posição e o retorna. Se nenhum item é fornecido o último é removido. (os colchetes em volta do indicam que esse parâmetro é opcional)


In [139]:
lista = [1, 2, 3, 4, 5]
lista.pop()


Out[139]:
5

In [140]:
lista


Out[140]:
[1, 2, 3, 4]

In [141]:
lista.pop(0)


Out[141]:
1

In [142]:
lista


Out[142]:
[2, 3, 4]

lista.extend(L) - Extende a lista anexando todos os elementos de L à lista.


In [143]:
lista = [1, 2, 3]
lista


Out[143]:
[1, 2, 3]

In [144]:
lista.extend([4, 5, 6])
lista


Out[144]:
[1, 2, 3, 4, 5, 6]

lista.index(x) - Fornece o índice (ou posição) do elemento x na lista


In [145]:
lista = [1, 2, 3, 4, 5]
lista.index(3)


Out[145]:
2

In [146]:
lista.index(5)


Out[146]:
4

lista.count(x) - Conta a quantidade de ocorrências do elemento x na lista


In [147]:
lista = [1, 1, 2, 3, 3, 3]
lista.count(2)


Out[147]:
1

In [148]:
lista.count(1)


Out[148]:
2

In [149]:
lista.count(3)


Out[149]:
3

lista.sort() - Ordena a lita in-place. Para saber mais sobre operações in-place veja este post do Rafael Correia.


In [150]:
lista = [3, 1, 7, 0, 6]
lista


Out[150]:
[3, 1, 7, 0, 6]

In [151]:
lista.sort()  # ordena lista in-place

lista


Out[151]:
[0, 1, 3, 6, 7]

sorted() - ordena sequências sem ser in-place


In [152]:
lista = [8, 2, 4, 1, 3]

sorted(lista)  # ordena lista


Out[152]:
[1, 2, 3, 4, 8]

In [153]:
lista  # sorted não altera o valor da lista


Out[153]:
[8, 2, 4, 1, 3]

In [154]:
s = 'dcba'
sorted(s)  # ordena todas as sequências e não só listas


Out[154]:
['a', 'b', 'c', 'd']

lista.reverse() - reverte a ordem da lista in-place.


In [155]:
lista = [1, 2, 3, 4, 5]

lista.reverse()  # reverte a lista in-place
lista


Out[155]:
[5, 4, 3, 2, 1]

reversed() - reverte a ordem de uma sequência. Não é in-place.


In [156]:
lista = [1, 2, 3, 4, 5]
list(reversed(lista))


Out[156]:
[5, 4, 3, 2, 1]

In [157]:
lista


Out[157]:
[1, 2, 3, 4, 5]

In [158]:
s = 'abcdefg'
list(reversed(s))  # reverted todas as sequências e não só listas


Out[158]:
['g', 'f', 'e', 'd', 'c', 'b', 'a']

Iterando listas:


In [159]:
numeros = [1, 2, 3, 4, 5]
for num in numeros:
    print(num)


1
2
3
4
5

In [ ]:

lista = ['sou uma string', -1.5, 10, 'outra string', False] for elemento in lista: print(elemento)

Iteração ao contrário:


In [161]:
lista = ['sou uma string', -1.5, 10, 'outra string', False]
for elemento in reversed(lista):  # usamos a função reversed!
    print(elemento)


False
outra string
10
-1.5
sou uma string

Iteração de uma sequência de números:


In [162]:
for i in range(5):
    print(i)


0
1
2
3
4

Mais sobre a fução range(inicio, fim, passo) que gera uma lista de números começando de início, indo até fim, andando "passo" vezes.

Seu uso é demonstrado a seguir:


In [163]:
intervalo = range(5, 10)  # intervalo de 5 a 9
list(intervalo)


Out[163]:
[5, 6, 7, 8, 9]

In [164]:
intervalo = range(0, 50, 5)  # intervalo de 0 a 50 de 5 em 5
list(intervalo)


Out[164]:
[0, 5, 10, 15, 20, 25, 30, 35, 40, 45]

In [165]:
intervalo = range(-10, -50, -7)  # intervalo de -10 a -50
list(intervalo)


Out[165]:
[-10, -17, -24, -31, -38, -45]

Iteração pelos índices:


In [166]:
lista = ['Estou', 'sem', 'criativdade', 'para', 'criar', 'lista']

for i in range(len(lista)):
    print(i, lista[i])


0 Estou
1 sem
2 criativdade
3 para
4 criar
5 lista

List comprehension

List comprehension fornece uma maneira simples de de criar listas. É geralmente utilizada para criar novas listas que os elementes resultantes vem de operações aplicadas em cima da lista original. Também pode ser usada para criar sublistas desses elementos que satisfaçam certa condição.

Por exemplo para conseguir uma lista de quadrados a partir de uma lista de números:


In [167]:
numeros = [1, 2, 3, 4, 5, 6, 7]

quadrados = []
for numero in numeros:
    quadrados.append(numero ** 2)

quadrados


Out[167]:
[1, 4, 9, 16, 25, 36, 49]

Usando list comprehensions fica assim:


In [174]:
numeros = [1, 2, 3, 4, 5, 6, 7]

quadrados = [numero ** 2 for numero in numeros]  # mais legível

quadrados


Out[174]:
[1, 4, 9, 16, 25, 36, 49]

Números ímpares:


In [169]:
numeros = [1, 2, 3, 4, 5, 6, 7]

impares = [numero for numero in numeros if numero % 2 == 1]

impares


Out[169]:
[1, 3, 5, 7]

É o mesmo que:


In [170]:
numeros = [1, 2, 3, 4, 5, 6, 7]

impares = []
for numero in numeros:
    if numero % 2 == 1:
        impares.append(numero)

impares


Out[170]:
[1, 3, 5, 7]

Cuidado!

Tome cuidado ao criar listas de listas usando repetição de listas com o operador *:


In [15]:
matriz = [[]] * 5
matriz


Out[15]:
[[], [], [], [], []]

In [185]:
matriz[0].extend([3, 1])
matriz


Out[185]:
[[3, 1], [3, 1], [3, 1], [3, 1], [3, 1]]

In [186]:
matriz[2].pop()
matriz


Out[186]:
[[3], [3], [3], [3], [3]]

Isso acontece pois a lista interna é criada apenas uma vez, quando o correto é criar 10 listas. Uma maneira elegante de fazer isso corretamente é usando list comprehension:


In [187]:
matriz = [[] for _ in range(5)]
matriz


Out[187]:
[[], [], [], [], []]

In [188]:
matriz[0].extend([3, 1])
matriz


Out[188]:
[[3, 1], [], [], [], []]

In [189]:
matriz[0].pop()
matriz


Out[189]:
[[3], [], [], [], []]

No exemplo anterior aprendemos como declarar uma matriz vazia, porém caso queiramos criar uma matriz de tamanho fixo populadas por um valor padrão usamos aninhamento de list comprehension.

Para criar uma matriz 5x5 preenchida com zero:


In [18]:
[[0 for _ in range(5)] for _ in range(5)]


Out[18]:
[[0, 0, 0, 0, 0],
 [0, 0, 0, 0, 0],
 [0, 0, 0, 0, 0],
 [0, 0, 0, 0, 0],
 [0, 0, 0, 0, 0]]

Exercícios

  • Complete a função para calcular a soma de uma lista de números dada:

In [1]:
def soma(numeros):
    soma = 0
    for numero in numeros:
        soma += numero
    return soma

In [2]:
# Testes para verificar a corretude da função soma
assert soma([3021.833, 8787.958, 8701.609, 9607.527, 2105.801]) == 32224.728000000003
assert soma([8612.141, -5273.932]) == 3338.209
assert soma([-8369.594, -5766.904, 7128.654]) == -7007.843999999999
assert soma([-6493.119, 647.921, -1889.898, 2814.045, 1443.082, -9671.503]) == -13149.472
assert soma([-4065.074, 8418.62]) == 4353.546
assert soma([2647.4, -3745.222, -1967.752]) == -3065.574
assert soma([-462.477, -4756.437, -6841.597]) == -12060.510999999999
assert soma([-9049.308, -8559.362]) == -17608.67
assert soma([-1311.291, 3207.467, -2685.757, 7758.034, 6556.007, 5677.477]) == 19201.936999999998
assert soma([7592.047, 8226.455, 5889.92, 3802.669]) == 25511.091
  • Complete a função pra encontrar o menor (mínimo) valor de uma lista:

In [3]:
def minimo(numeros):
    minimo = numeros[0]
    for numero in numeros[1:]:
        if numero < minimo:
            minimo = numero
    return minimo

In [4]:
# Testes para verificar a corretude da função soma
assert minimo([-6473.695, 438.199, 7959.093, 7842.664, 7233.021, -4149.884]) == -6473.695
assert minimo([6505.843, -7075.108]) == -7075.108
assert minimo([9151.244, -4605.586, -2920.8]) == -4605.586
assert minimo([-6586.813, -2264.44, 4460.277]) == -6586.813
assert minimo([1553.196, 1631.333]) == 1553.196
assert minimo([6876.253, 9349.21, -382.322]) == -382.322
assert minimo([9604.147, -378.112, 2574.795, -5597.589, 8751.16]) == -5597.589
assert minimo([2761.954, 4439.859, 9361.367, 884.972]) == 884.972
assert minimo([2989.882, 346.282, 9051.012, 4973.448, 1821.907]) == 346.282
assert minimo([4996.027, -5269.592]) == -5269.592

Agora que você já fez esses maravilhosos exercícios, vamos ver algumas funções úteis do Python:

sum(sequencia) - retorna a soma de uma sequência de números


In [190]:
numeros = [1, 2, 3, 4]
sum(numeros)


Out[190]:
10

In [191]:
numeros = [684.97, 514.68, 475.69, 194.07]
sum(numeros)


Out[191]:
1869.41

min(sequencia) e max(sequencia) - retorna, respectivamente, o elemento mínimo e máximo da sequência dada


In [192]:
numeros = [1, 2, 3, 4]
min(numeros)


Out[192]:
1

In [193]:
max(numeros)


Out[193]:
4

In [194]:
min('abracadabra')


Out[194]:
'a'

In [195]:
max('abracadabra')


Out[195]:
'r'

all(sequencia) - retorna True se todos os valores da sequência forem verdadeiros


In [196]:
seq = [True, True, False, False, True]
all(seq)


Out[196]:
False

In [197]:
seq = [True] * 10
seq


Out[197]:
[True, True, True, True, True, True, True, True, True, True]

In [198]:
all(seq)


Out[198]:
True

In [199]:
numeros = [-2, 9, 0, 4, -7]
all(numeros)


Out[199]:
False

any(sequencia) - retorna True se algum dos valores da sequência for verdadeiro


In [200]:
seq = [False, True, False, False, True]
any(seq)


Out[200]:
True

In [201]:
seq = [False] * 10
seq


Out[201]:
[False, False, False, False, False, False, False, False, False, False]

In [202]:
any(seq)


Out[202]:
False

In [203]:
numeros = [0, 9, 0, 0, -7]
any(numeros)


Out[203]:
True

round(numero, casas) - Arredonda um número para a quantidade de casas fornecido à função


In [204]:
import random

random.random()  # gera um número aleatório entre 0 e 1


Out[204]:
0.3241401787164989

In [205]:
numero = random.random() * 1000  # multiplica por 1000 para gerar um número com 3 dígitos significantes
numero


Out[205]:
712.9719596027301

In [206]:
round(numero, 2)


Out[206]:
712.97

In [207]:
round(numero, 5)


Out[207]:
712.97196

Extra: também é possível arredondar os dígitos significatios especificando uma quantidade negativa de casas:


In [208]:
numero = 431637

In [209]:
round(numero, -1)


Out[209]:
431640

In [210]:
round(numero, -2)


Out[210]:
431600

In [211]:
round(numero, -3)


Out[211]:
432000

Exemplos

Usando as funções apresentadas é simples calcular a médida de, por exemplo, uma lista de notas:


In [212]:
# gerando aleatóriamente as notas
import random

notas = [random.random() * 10 for i in range(5)]  # gera 5 notas aleatórias
notas = [round(nota, 1) for nota in notas]  # arredonda as notas para 1 casa decimal
notas


Out[212]:
[2.6, 6.8, 8.7, 4.3, 7.7]

In [213]:
media = sum(notas) / len(notas)
media


Out[213]:
6.0200000000000005

Mais fácil que:


In [214]:
soma_notas = 0
for nota in notas:
    soma_notas += nota
soma_notas / len(notas)


Out[214]:
6.0200000000000005

Agora vamos separar as notas em duas listas, uma de aprovados (nota >= 5) e outra de reprovados


In [215]:
aprovados = [nota for nota in notas if nota >= 5]
aprovados


Out[215]:
[6.8, 8.7, 7.7]

In [216]:
reprovados = [nota for nota in notas if nota < 5]
reprovados


Out[216]:
[2.6, 4.3]

Com list comprehension fica fácil filtrar os arquivos .txt de um diretório. Para isso usaremos a função listdir() do módulo os que lista todos os arquivos de um diretório e a função str.endswith() de strings que compara o final da string com uma substring.


In [3]:
import os

os.listdir('..')  # pegando arquivos do diretório acima do diretório deste notebook


Out[3]:
['requirements.txt',
 'README.md',
 '.gitignore',
 'aula-01',
 '.git',
 'lista.md',
 'aula-03',
 '.requirements.txt.un~',
 'aula-02']

In [5]:
[nome for nome in os.listdir('..') if nome.endswith('.txt')]


Out[5]:
['requirements.txt']

Suponhamos que administramos um sistema de gerenciamento de arquivos e um usuário envia 5 arquivos pesados. Precisamos verificar se esses arquivos cabem no espaço livre do sistema de arquivos para não dar problemas no servidor. Além disso também precisamos calcular a porcentagem de uso do espaço que cada arquivo ocupa.

Supondo que o sistema de arquivos esteja livre (ou seja, o espaço livre é o espaço total) essa verificação pode ser feita assim:


In [217]:
espaço_total = 1500000  # espaço livre total do sistema de arquivos em KB
tamanhos_arquivos = [388428.5, 189310.1, 310710.3, 160701, 898853.6]  # tamanhos em KB dos arquivos enviados pelo usuário

porcentagens_uso = [tamanho / espaço_total for tamanho in tamanhos_arquivos]
porcentagens_uso = [round(porcentagem * 100, 2) for porcentagem in porcentagens_uso]
porcentagens_uso


Out[217]:
[25.9, 12.62, 20.71, 10.71, 59.92]

Esses arquivos cabem no espaço fornecido?


In [218]:
sum(porcentagens_uso) <= 100


Out[218]:
False

Esses arquivos ocupam mais que 100% do espaço, então não cabe.

Exercícios

  • Escreva uma função que receba um diretório (ou seu caminho relativo como no exemplo anterior) e liste os arquivos com a extensão oferecida.

In [ ]:
def lista_arquivos_por_extensao(diretorio, extensao):
    ...

Para testar essa função precisariamos usar mock para mudar o efeito da função os.listdir(), portanto dessa vez você mesmo terá que testar sua função.

  • Escreva uma função que gere uma matriz. Essa função deve receber os argumentos m e n que representam o tamanho da matriz e um argumento default para que essa matriz seja inicializada com esses valores

In [6]:
def gera_matriz(m, n, default):
    ...

Tuplas

Tupla é uma sequência imutável. Ela pode ser usada como listas imutáveis ou como registros sem nomes de campos.


In [8]:
coordenadas = (40.716667, -74)
coordenadas


Out[8]:
(40.716667, -74)

In [9]:
type(coordenadas)


Out[9]:
tuple

In [220]:
coordenadas[0]


Out[220]:
40.716667

In [221]:
coordenadas[1]


Out[221]:
-74

Tuplas não podem ser modificadas:


In [222]:
coordenadas[0] = 10.5931


---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-222-472d761b3967> in <module>()
----> 1 coordenadas[0] = 10.5931

TypeError: 'tuple' object does not support item assignment

Tuplas também podem ser criadas sem o parêntes, pois o que realmente as define são as vírgulas:


In [11]:
tupla = 'a', 'b', 'c', 'd'
tupla


Out[11]:
('a', 'b', 'c', 'd')

É o mesmo que:


In [12]:
tupla = ('a', 'b', 'c', 'd')
tupla


Out[12]:
('a', 'b', 'c', 'd')

Slices também funcionam em tuplas:


In [13]:
tupla[:2]


Out[13]:
('a', 'b')

Podemos fazer listas de tuplas para armazenar informações que não mudam, como:


In [223]:
cidades = [
    ('Nova Iorque', 'E.U.A', (40.716667, -74)),
    ('Tóquio', 'Japão', (35.683333, 139.683333)),
    ('São Paulo', 'Brasil', (-23.547778, -46.635833))
]

cidades


Out[223]:
[('Nova Iorque', 'E.U.A', (40.716667, -74)),
 ('Tóquio', 'Japão', (35.683333, 139.683333)),
 ('São Paulo', 'Brasil', (-23.547778, -46.635833))]

In [224]:
cidades[1]


Out[224]:
('Tóquio', 'Japão', (35.683333, 139.683333))

In [225]:
cidades[1][1]


Out[225]:
'Japão'

Por ser uma sequência a tupla suporta diversas operações como:


In [226]:
tupla = ('Abc', False, 100)
for item in tupla:
    print(item)


Abc
False
100

In [227]:
all(tupla)


Out[227]:
False

In [228]:
any(tupla)


Out[228]:
True

In [229]:
valores = (34, 40.1, -10.404)
sum(valores)


Out[229]:
63.696

In [230]:
min(valores)


Out[230]:
-10.404

In [231]:
max(valores)


Out[231]:
40.1

In [232]:
len(valores)


Out[232]:
3

Tuplas podem ser aninhadas:


In [233]:
dados = ('Cidadopolis', 'Paisópolis')
cidade = dados, (40.716667, -74)  # aninha a dados e coordenada dentro da tupla cidade
cidade


Out[233]:
(('Cidadopolis', 'Paisópolis'), (40.716667, -74))

Tuplas podem conter objetos mutáveis como listas:


In [234]:
t = ([1, 2, 3], [3, 2, 1])
t


Out[234]:
([1, 2, 3], [3, 2, 1])

Os objetos mutáveis dentro da tupla podem ser modificados:


In [235]:
t[0][0] = 100
t


Out[235]:
([100, 2, 3], [3, 2, 1])

In [236]:
t[1][2] = -5
t


Out[236]:
([100, 2, 3], [3, 2, -5])

Mas não a tupla:


In [237]:
t[0] = [1, 2, 3]


---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-237-c8cbbb9bb99f> in <module>()
----> 1 t[0] = [1, 2, 3]

TypeError: 'tuple' object does not support item assignment

"Mágica" do Python: desempacotamento de tuplas


In [238]:
cidade = ('Nova Iorque', 'E.U.A', (40.716667, -74))
cidade


Out[238]:
('Nova Iorque', 'E.U.A', (40.716667, -74))

In [239]:
nome, pais, coordenadas = cidade
nome


Out[239]:
'Nova Iorque'

In [240]:
pais


Out[240]:
'E.U.A'

In [241]:
coordenadas


Out[241]:
(40.716667, -74)

Iso permite iterar sequências de uma maneira mais semântica, conforme exemplo a seguir.

Como é feito normalmente:


In [242]:
cidades = [
    ('Nova Iorque', 'E.U.A', (40.716667, -74)),
    ('Tóquio', 'Japão', (35.683333, 139.683333)),
    ('São Paulo', 'Brasil', (-23.547778, -46.635833))
]

for cidade in cidades:
    print(cidade[1])


E.U.A
Japão
Brasil

Usando o empacotamento de tuplas:


In [243]:
for nome, pais, coordenadas in cidades:
    print(pais)


E.U.A
Japão
Brasil

Ou ainda:


In [244]:
cidades


Out[244]:
[('Nova Iorque', 'E.U.A', (40.716667, -74)),
 ('Tóquio', 'Japão', (35.683333, 139.683333)),
 ('São Paulo', 'Brasil', (-23.547778, -46.635833))]

In [245]:
for nome, pais, (x, y) in cidades:
    print(pais)


E.U.A
Japão
Brasil

In [246]:
for nome, pais, (x, y) in cidades:
    print(x)


40.716667
35.683333
-23.547778

Fim da aula 03

Não esqueça de tentar resolver as listas de exercícios que estão no repositório do github. Para aprender uma linguagem de verdade é preciso programar.